/*
 *  Transaction.cpp
 *  
 *
 *  Created by Zhenjie Zhang on 12/21/11.
 *  Copyright 2011 Thothe. All rights reserved.
 *  Note that all lock and unlock operations are done in this class.
 */

#include "access/Transaction.h"
#include "access/DatabaseManager.h"
#include "access/db.h"
#include "storage/DatumPage.h"

#include <iostream>

using namespace std;

Transaction::Transaction()
{
	lockStack = new stack<LockEntry>;
	lockMap = new map<void*,void*>;
}

Transaction::~Transaction()
{
    if(!lockStack->empty())
    {
        // this is not supposed to happen
        // we have to abort all things and locks in the stack
        
        Abort();
    }

	delete lockStack;
	delete lockMap;
}

bool Transaction::AddDatumPage(pthread_mutex_t* l, void* originalPage, void* copyPage)
{
    LockEntry ent;
    
    ent.type = DB_PAGE;
    
    ent.objLock = l;
    ent.originalObj = originalPage;
    ent.copyObj = copyPage;
    
    lockStack->push(ent);

    lockMap->insert(pair<void*, void*>(originalPage, copyPage));
    
    return true;
}

bool Transaction::AddDatabase(pthread_mutex_t* l, void* originalDB, void* copyDB)
{
    LockEntry ent;
    
    ent.type = DB_INS;
    ent.objLock = l;
    ent.originalObj = originalDB;
    ent.copyObj = copyDB;

    lockStack->push(ent);
    
    lockMap->insert(pair<void*, void*>(originalDB, copyDB));

    return true;
}

bool Transaction::AddDBManager(pthread_mutex_t* l, void* orgDBMan, void* copyDBMan)
{
    LockEntry ent;
    
    ent.type = DB_MAN;
    ent.objLock = l;
    ent.originalObj = orgDBMan;
    ent.copyObj = copyDBMan;

    lockStack->push(ent);

    lockMap->insert(pair<void*, void*>(orgDBMan, copyDBMan));

    return true;
}

bool Transaction::Commit()
{
    LockEntry topEntry;
    

    while(!lockStack->empty())
    {
        topEntry = lockStack->top();
        
        if(topEntry.type == DB_PAGE)
        {
            DatumPage* org_dp = (DatumPage*)topEntry.originalObj;
            DatumPage* dp = (DatumPage*)topEntry.copyObj;
            cout << "  DB_PAGE : " << org_dp << " " << dp << endl;
            
			if(org_dp)
			{
				DatumPage::CopyDatumPage(org_dp, dp);
            
				delete dp;
			}
        }
        else if(topEntry.type == DB_INS)
        {
            Database* org_db = (Database*)topEntry.originalObj;
            Database* db = (Database*)topEntry.copyObj;
            cout << "  DB_INS : " << org_db << " " << db << endl;

            if(org_db)
			{
				Database::CopyDatabase(org_db, db);
				delete db;
			}
        }
        else if(topEntry.type == DB_MAN)
        {
            DatabaseManager* org_dman = (DatabaseManager*)topEntry.originalObj;
            DatabaseManager* dman = (DatabaseManager*)topEntry.copyObj;
            cout << "  DB_MAN : " << org_dman << " " << dman << endl;
            
			if(org_dman)
			{
				DatabaseManager::CopyDatabaseManager(org_dman, dman);
            
				delete dman;
			}
        }

        pthread_mutex_unlock(topEntry.objLock);
        
        lockStack->pop();
    }
    
    cout << "Commitment done" << endl;
    
    return true;
}

bool Transaction::AbortLast(void* pt)
{
    LockEntry topEntry;
    
    if(lockStack->empty())
        return false;

    if(lockMap->size() == 0)
        return false;
    
    topEntry = lockStack->top();
    
    pthread_mutex_unlock(topEntry.objLock);
    
    lockStack->pop();
    
    int last_item = lockMap->size();
    
    lockMap->erase(pt);
    
    return true;    
}

bool Transaction::Abort()
{
    LockEntry topEntry;
    
    while(!lockStack->empty())
    {
        topEntry = lockStack->top();
        
        if(topEntry.type == DB_PAGE)
        {
            DatumPage* dp = (DatumPage*)topEntry.copyObj;
            //cout << "deleting " << dp << endl;   
            delete dp;
        }
        else if(topEntry.type == DB_INS)
        {
            Database* db = (Database*)topEntry.copyObj;
            //cout << "deleting " << db << endl;   
            
            delete db;
        }
        else if(topEntry.type == DB_MAN)
        {
            DatabaseManager* dman = (DatabaseManager*)topEntry.copyObj;
            //cout << "deleting " << dman << endl;   

            delete dman;
        }
        
        pthread_mutex_unlock(topEntry.objLock);
        
        lockStack->pop();
    }
    
    return true;
}

// the following function is used to find the dman/db/dp, which is already obtained and copied
// by the current transaction. if it is not available, return null pointer. otherwise, 
// return the point to the copy.
void* Transaction::FindLink(void* pt)
{
    //cout << lockMap->size() << endl;
    map<void*, void*>::iterator it;
    
    it = lockMap->find(pt);
    
    if(it == lockMap->end())
        return NULL;
    
    return it->second;
}
